package com.serviceprovider.business.module.report.services.dao.impl;

//~--- non-JDK imports --------------------------------------------------------

import com.serviceprovider.business.module.admin.settings.services.ISettingsService;
import com.serviceprovider.business.module.report.services.dao.interfaces.IReportService;
import com.serviceprovider.business.module.report.valueobjects.AllFeeBatchRecord;
import com.serviceprovider.business.module.report.valueobjects.AllFeeReportData;
import com.serviceprovider.business.module.report.valueobjects.AllFeeTransactionRecord;
import com.serviceprovider.business.module.report.valueobjects.ReportGenerationCriteria;
import com.serviceprovider.business.module.report.valueobjects.SelfFeeReportData;
import com.serviceprovider.business.module.report.valueobjects.TransactionsReportData;
import com.serviceprovider.business.module.report.valueobjects.TransactionsReportRecord;
import com.serviceprovider.business.module.sp.preferences.dao.interfaces.IPreferencesDao;
import com.serviceprovider.business.module.sp.preferences.model.Preferences;
import com.serviceprovider.business.module.sp.transaction.model.Batch;
import com.serviceprovider.business.module.sp.transaction.model.TransactionCriteria;
import com.serviceprovider.business.module.sp.transaction.model.TransactionDetail;
import com.serviceprovider.business.module.sp.transaction.model.TransactionUserAmount;
import com.serviceprovider.business.module.sp.transaction.services.dao.interfaces.ITransactionDao;
import com.serviceprovider.business.module.sp.transaction.services.dao.interfaces.ITransactionUserAmountDao;

import org.apache.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

//~--- JDK imports ------------------------------------------------------------

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author mrduongnv@gmail.com
 *
 */
@Service("reportService")
public class ReportServiceImpl implements IReportService {
    private static Logger     logger = Logger.getLogger(ReportServiceImpl.class);
    @Autowired
    IPreferencesDao           preferencesService;
    @Autowired
    ISettingsService          propertiesService;
    @Autowired
    private ITransactionDao   transactionDao;
    @Autowired
    ITransactionUserAmountDao transactionUserAmountDao;

    @Override
    public SelfFeeReportData getSelfFeeReportData(ReportGenerationCriteria criteria) {
        if (logger.isDebugEnabled()) {
            logger.debug("Start to load data for self fee report");
        }

        TransactionCriteria                 transCriteria      = transalteToTransactionCriteria(criteria);
        double                              serviceFee         = getServiceFee(transCriteria.getProviderId());
        List<TransactionDetail>             transactionDetails = transactionDao.findByCriteria(transCriteria);
        Map<Batch, List<TransactionDetail>> batchTrans         = seperateByBatch(transactionDetails);
        SelfFeeReportData                   batchResults       = new SelfFeeReportData();

        for (Entry<Batch, List<TransactionDetail>> entry : batchTrans.entrySet()) {
            TransactionsReportData         result          = new TransactionsReportData();
            int                            selfFeeTotals   = 0;
            int                            totalAmount     = 0;
            double                         totalServiceFee = 0;
            List<TransactionsReportRecord> records         = new ArrayList<TransactionsReportRecord>();

            for (TransactionDetail transaction : entry.getValue()) {
                TransactionsReportRecord    record      = new TransactionsReportRecord();
                List<TransactionUserAmount> transAmount =
                    transactionUserAmountDao.findByTransactionDetail(entry.getKey(), transaction);

                record.setTransactionUserAmounts(transAmount);
                record.setSsn(String.valueOf(transaction.getCustomerSSN()));
                record.setCustomerName(transaction.getCustomerName());
                record.setSelfFee(transaction.getSelfFee());
                record.setAmount(transaction.getAmount());
                record.setServiceFee(serviceFee);
                records.add(record);
                totalAmount     += transaction.getAmount();
                selfFeeTotals   += transaction.getSelfFee();
                totalServiceFee += serviceFee;
            }

            result.setSelfFeeTotals(selfFeeTotals);
            result.setTotalAmount(totalAmount);
            result.setTotalServiceFee(totalServiceFee);
            result.setRecords(records);
            result.setBatch(entry.getKey());
            batchResults.addTransReportData(result);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("End of loading data for self fee report");
        }

        return batchResults;
    }

    private Map<Batch, List<TransactionDetail>> seperateByBatch(List<TransactionDetail> transactionDetails) {
        Map<Batch, List<TransactionDetail>> result       = new LinkedHashMap<Batch, List<TransactionDetail>>();
        Map<Batch, List<TransactionDetail>> unSortResult = new HashMap<Batch, List<TransactionDetail>>();

        for (TransactionDetail trans : transactionDetails) {
            List<TransactionDetail> transList = unSortResult.get(trans.getBatch());

            if (transList == null) {
                transList = new ArrayList<TransactionDetail>();
                unSortResult.put(trans.getBatch(), transList);
            }

            transList.add(trans);
        }

        List<Batch> batchs = new ArrayList<Batch>(unSortResult.keySet());

        Collections.sort(batchs, new Comparator<Batch>() {
            @Override
            public int compare(Batch o1, Batch o2) {
                return o1.getId().compareTo(o2.getId());
            }
        });

        for (Batch batch : batchs) {
            result.put(batch, unSortResult.get(batch));
        }

        return result;
    }

    private TransactionCriteria transalteToTransactionCriteria(ReportGenerationCriteria criteria) {
        TransactionCriteria transCriteria = new TransactionCriteria();

        transCriteria.setDateFrom(criteria.getDateFrom());
        transCriteria.setDateTo(criteria.getDateTo());
        transCriteria.setProviderId(criteria.getProviderId());
        transCriteria.setPayeesId(criteria.getPayees());
        transCriteria.setTransStatusId(criteria.getStatus());

        return transCriteria;
    }

    @Override
    public AllFeeReportData getCustomFeesReportData(ReportGenerationCriteria criteria, List<String> selectedFee) {
        double                              serviceFee          = getServiceFee(criteria.getProviderId());
        TransactionCriteria                 transactionCriteria = transalteToTransactionCriteria(criteria);
        List<TransactionDetail>             transactions        = transactionDao.findByCriteria(transactionCriteria);
        Map<Batch, List<TransactionDetail>> batchTransactions   = seperateByBatch(transactions);
        AllFeeReportData                    reportData          = new AllFeeReportData();

        for (Entry<Batch, List<TransactionDetail>> entry : batchTransactions.entrySet()) {
            reportData.setFeeNames(selectedFee);

            AllFeeBatchRecord             batchRecord           = new AllFeeBatchRecord();
            List<AllFeeTransactionRecord> allTransactionRecords = new ArrayList<AllFeeTransactionRecord>();

            for (TransactionDetail trans : entry.getValue()) {
                AllFeeTransactionRecord     transRecord = new AllFeeTransactionRecord();
                List<TransactionUserAmount> transAmount =
                    transactionUserAmountDao.findByTransactionDetail(entry.getKey(), trans);

                transRecord.setTransactionUserAmounts(transAmount);

                HashMap<String, Integer> fees = new HashMap<String, Integer>(trans.getFees());

                for (String feeKey : trans.getFees().keySet()) {
                    if (!selectedFee.contains(feeKey)) {
                        fees.remove(feeKey);
                    }
                }

                transRecord.setFees(fees);
                transRecord.setAmount(trans.getAmount());
                transRecord.setCustomerSSN(trans.getCustomerSSN());
                transRecord.setCustomerName(trans.getCustomerName());
                transRecord.setServiceFee(serviceFee);

                // Added to show custom label - start
                transRecord.setCustomLabel1(trans.getCustomLabel1());
                transRecord.setCustomLabel2(trans.getCustomLabel2());

                // Added to show custom label - end
                allTransactionRecords.add(transRecord);
            }

            batchRecord.setTransactionRecords(allTransactionRecords);
            batchRecord.setBatch(entry.getKey());
            reportData.addBatchRecord(batchRecord);
        }

        return reportData;
    }

    /**
     * Return the service fee of a specific Service Provider
     */
    private double getServiceFee(Integer spId) {
        Preferences preferences = preferencesService.findByUserId(spId);

        if ((preferences == null) || (preferences.getCustomFee() == 0)) {
            return propertiesService.getDefaultFee() / 100.0;
        } else {
            return preferences.getCustomFee();
        }
    }
}



